# Check requisite packages are installed.
Warning messages:
1: Unknown or uninitialised column: `SeedRuns`. 
2: Unknown or uninitialised column: `SeedRunsNum`. 
3: Unknown or uninitialised column: `Abund`. 
packages <- c(
  "plotly",
  "dplyr"
)
for (pkg in packages) {
  library(pkg, character.only = TRUE)
}
package 㤼㸱plotly㤼㸲 was built under R version 4.0.5Loading required package: ggplot2
package 㤼㸱ggplot2㤼㸲 was built under R version 4.0.3
Attaching package: 㤼㸱plotly㤼㸲

The following object is masked from 㤼㸱package:ggplot2㤼㸲:

    last_plot

The following object is masked from 㤼㸱package:stats㤼㸲:

    filter

The following object is masked from 㤼㸱package:graphics㤼㸲:

    layout

package 㤼㸱dplyr㤼㸲 was built under R version 4.0.4
Attaching package: 㤼㸱dplyr㤼㸲

The following objects are masked from 㤼㸱package:stats㤼㸲:

    filter, lag

The following objects are masked from 㤼㸱package:base㤼㸲:

    intersect, setdiff, setequal, union

Load

Pulling code almost directly from LM1996-NumPoolCom-QDatMake-2021-05.Rmd.

dirViking <- c(
  file.path(
    getwd(), "LCAB_LawMorton1996-NumericalPoolCommunityScaling5"
  )
)
dirVikingResults <- file.path(
  dirViking, paste0(
    "save-", c(
      "2021-05-19",
      "2021-05-21",
      "2021-05-24"
    )
  )
)
resultFormat <- paste0(
  "run-",
  "%d", # Combination Number, or CombnNum.
  "-",
  "%s", # Run Seed.
  ".RDS"
)

Data

source(
  file.path(getwd(),
            "LawMorton1996-NumericalPoolCommunityScaling-Settings5.R")
)

paramFrame <- with(list(
  b = rep(basal, times = length(consumer)),
  c = rep(consumer, each = length(basal)),
  s1 = seedsPrep[1:(length(basal) * length(consumer))],
  s2 = seedsPrep[
    (length(basal) * length(consumer) + 1):(
      2 * length(basal) * length(consumer))
  ],
  sR = seedsRun
), {
  temp <- data.frame(
    CombnNum = 0,
    Basals = b,
    Consumers = c,
    SeedPool = s1,
    SeedMat = s2,
    SeedRuns = "",
    SeedRunsNum = 0,
    EndStates = I(rep(list(""), length(b))),
    EndStatesNum = 0,
    EndStateSizes = I(rep(list(""), length(b))),
    EndStateSizesNum = NA,
    EndStateAssembly = I(rep(list(""), length(b))),
    EndStateAbundance = I(rep(list(""), length(b))),
    Dataset = "2021-05:5:Connectance",
    DatasetID = 5,
    stringsAsFactors = FALSE
  )
  for (i in 1:nrow(temp)) {
    seeds <- sR[((i - 1) * runs + 1) : (i * runs)]
    temp$SeedRuns[i] <- toString(seeds) # CSV
    temp$SeedRunsNum[i] <- length(seeds)
  }
  temp$CombnNum <- 1:nrow(temp)
  temp
})
# Note: n + 2 end states. Failure to finish, failure to obtain state, and state.
# Modified from above, but with the abundance recorded.
for (i in 1:nrow(paramFrame)) {
  resultsList <- list(
    "No Run" = 0,
    "No State" = 0
  )
  resultsSize <- list(
    "0" = 0
  )
  resultsAssembly <- list(
    "No Run" = data.frame(),
    "No State" = data.frame()
  )
  resultsAbund <- list(
    "No Run" = "",
    "No State" = ""
  )
  seeds <- unlist(strsplit(paramFrame$SeedRuns[i], ', '))
  for (seed in seeds) {
    fileName <- file.path(
      dirVikingResults,
      sprintf(resultFormat, paramFrame$CombnNum[i], seed)
    )

    fileName <- fileName[file.exists(fileName)]
    
    if (length(fileName) >= 1) {
      if (length(fileName) == 2) {
        temp <- load(fileName[1])
        temp <- eval(parse(text = temp))
        temp2 <- load(fileName[2])
        temp2 <- eval(parse(text = temp2))
        if (!identical(temp, temp2)) {
          stop("2 files, but not identical.")
        }
      } else if (length(fileName) > 2) {
        stop("At least 3 same files.")
      } else {
        temp <- load(fileName)
        temp <- eval(parse(text = temp)) # Get objects.
      }

      if (is.list(temp) && "Result" %in% names(temp)) {

        if (is.data.frame(temp$Result))
          community <- temp$Result$Community[[nrow(temp$Result)]]
        else
          community <- temp$Result

        size <- toString(length(community))

        if (community[1] != "")
          abund <- toString(temp$Abund[community + 1])
        else
          abund <- ""

        community <- toString(community)

        if (community == "") {
          resultsList$`No State` <- resultsList$`No State` + 1
          resultsSize$`0` <- resultsSize$`0` + 1

        } else if (community %in% names(resultsList)) {
          resultsList[[community]] <- resultsList[[community]] + 1
          resultsSize[[size]] <- resultsSize[[size]] + 1

        } else {
          resultsList[[community]] <- 1
          resultsAssembly[[community]] <- temp
          resultsAbund[[community]] <- abund

          if (size %in% resultsSize) {
            resultsSize[[size]] <- resultsSize[[size]] + 1
          } else {
            resultsSize[[size]] <- 1
          }
        }
      } else {
        resultsList$`No State` <- resultsList$`No State` + 1
        resultsSize$`0` <- resultsSize$`0` + 1
      }
    } else {
      resultsList$`No Run` <- resultsList$`No Run` + 1
      resultsSize$`0` <- resultsSize$`0` + 1
    }
  }

  paramFrame$EndStates[[i]] <- resultsList
  paramFrame$EndStatesNum[i] <- length(resultsList) - 2 # ! No State, No Run
  paramFrame$EndStateSizes[[i]] <- resultsSize
  paramFrame$EndStateSizesNum[i] <- length(resultsSize) - 1 # ! 0
  paramFrame$EndStateAssembly[[i]] <- resultsAssembly
  paramFrame$EndStateAbundance[[i]] <- resultsAbund
}

Plot

# X, Y, Basal and Consumer.
# Z = Sizes of the Endstates.

plotScalingData <- data.frame(
  CombnNum = rep(paramFrame$CombnNum, paramFrame$EndStatesNum),
  Basals = rep(paramFrame$Basals, paramFrame$EndStatesNum),
  Consumers = rep(paramFrame$Consumers, paramFrame$EndStatesNum),
  Dataset = rep(paramFrame$Dataset, paramFrame$EndStatesNum),
  DatasetID = rep(paramFrame$DatasetID, paramFrame$EndStatesNum)
)

# Communities
comms <- unlist(lapply(paramFrame$EndStates, names))
freqs <- unlist(paramFrame$EndStates)
asmbl <- unlist(paramFrame$EndStateAssembly, recursive = FALSE)
asmbl <- asmbl[comms != "No Run" & comms != "No State"]
freqs <- freqs[comms != "No Run" & comms != "No State"]
comms <- comms[comms != "No Run" & comms != "No State"]

asmbl <- lapply(asmbl, function(d) {
  if (is.null(d)) return(NA)
  if ("Result.Outcome" %in% names(d))
    d %>% dplyr::filter(Result.Outcome != "Type 1 (Failure)" &
                          Result.Outcome != "Present")
  else
    d$Result %>% dplyr::filter(Outcome != "Type 1 (Failure)" &
                                 Outcome != "Present")
})

plotScalingData$Communities <- comms
plotScalingData$CommunityFreq <- freqs
plotScalingData$CommunitySeq <- asmbl

# Community Size
temp <- unlist(lapply(strsplit(plotScalingData$Communities, ','), length))
plotScalingData$CommunitySize <- temp

# For usage by the reader.

plotScaling <- plotly::plot_ly(
  plotScalingData,
  x = ~Basals,
  y = ~Consumers,
  z = ~CommunitySize,
  color = ~Dataset,
  colors = c("red", "blue", "black")
)

plotScaling <- plotly::add_markers(plotScaling)

plotScaling <- plotly::layout(
  plotScaling,
  scene = list(
    xaxis = list(type = "log"),
    yaxis = list(type = "log"),
    camera = list(
      eye = list(
        x = -1.25, y = -1.25, z = .05
      )
    )
  )
)

plotScaling

Abundances

# > runif(1) * 1E8
# [1] 82598679
set.seed(82598679)

mats <- list()
poolsall <- list() # name pools used in save data; be careful!

for (i in 1:length(dirViking)) {
  temp <- load(file.path(
    dirViking[i],
    paste0("LawMorton1996-NumericalPoolCommunityScaling-PoolMats",
           5, #if (i > 1) i else "",
           ".RDS")
  ))
  mats[[i]] <- eval(parse(text = temp[1]))
  poolsall[[i]] <- eval(parse(text = temp[2]))
}
pools <- poolsall
candidateData <- plotScalingData %>% dplyr::group_by(
  CombnNum, Dataset
) %>% dplyr::mutate(
  OtherSteadyStates = dplyr::n() - 1
) %>% dplyr::filter(
  OtherSteadyStates > 0
)
candidateData %>% dplyr::select(-CommunitySeq)

Alas, there are 0 rows. So we are done, but perhaps we can close this file with some little thoughts and comments. For instance, we can recycle some earlier code to look at the two largest communities to see if there is anything interesting going on. We’ll load the abundances anyways.

candidateData <- plotScalingData %>% dplyr::group_by(
  CombnNum, Dataset
) %>% dplyr::mutate(
  OtherSteadyStates = dplyr::n() - 1
)
# First, check if it is in the paramFrame.
# Second, check if it is in the saved data from the previous.
# Otherwise, ignore it, we'll figure out what it is and why it is missing later.

candidateData$CommunityAbund <- ""

for (r in 1:nrow(candidateData)) {
  # ID 1:4 are used to identify paramFrame, 5 used to identify abundance
  ID <- candidateData[r, 1:6]
  paramFrameRow <- paramFrame %>% dplyr::filter(
    CombnNum == ID$CombnNum,
    Basals == ID$Basals,
    Consumers == ID$Consumers,
    Dataset == ID$Dataset
  )

  if (is.list(paramFrameRow$EndStateAbundance[[1]])) {
    entry <- which(ID$Communities == names(paramFrameRow$EndStateAbundance[[1]]))
    if (length(entry)) {
      candidateData$CommunityAbund[r] <- paramFrameRow$EndStateAbundance[[1]][[entry]]
      next()
    }
  }
}
candidateData <- candidateData %>% dplyr::filter(CommunityAbund != "",
                                                 CommunityAbund != "Failure")
candidateData$CommunityProd <- NA
for (r in 1:nrow(candidateData)) {
  candidateData$CommunityProd[r] <- with(
    candidateData[r, ],
    RMTRCode2::Productivity(
      Pool = pools[[1]][[CombnNum]],
      InteractionMatrix = mats[[1]][[CombnNum]],
      Community = Communities,
      Populations = CommunityAbund
    )
  )
}
candidateData

Graph

Taking code from LM1996-NumPoolCom-FoodWebs-2021-07.Rmd.

foodWebs <- list()

for (r in 1:nrow(candidateData)) {
  foodWebs[[r]] <- with(
    candidateData[r, ],
    {
      redCom <- RMTRCode2::CsvRowSplit(Communities)
      redMat <- mats[[1]][[CombnNum]][redCom, redCom]
      redPool <- pools[[1]][[CombnNum]][redCom, ]
      
      colnames(redMat) <- paste0('s',as.character(redCom))
      rownames(redMat) <- colnames(redMat)
      
      names(redPool)[1] <- "node"
      redPool$node <- colnames(redMat)
      names(redPool)[3] <- "M"
      
      Graph <- igraph::graph_from_adjacency_matrix(
        redMat, weighted = TRUE
      )
      
      Graph <- igraph::set.vertex.attribute(
        Graph, "name", value = colnames(redMat)
      )
      
      redPool$N <- RMTRCode2::CsvRowSplit(CommunityAbund)
      
      # For later analysis, take the matrix diagonal.
      
      redPool$Intraspecific <- diag(redMat)
      
      GraphAsDataFrame <- igraph::as_data_frame(Graph)
  
      # Add in abundances for calculating abundance * (gain or loss)
      GraphAsDataFrame <- dplyr::left_join(
        GraphAsDataFrame,
        dplyr::select(redPool, node, N),
        by = c("to" = "node")
      )
  
      # Split data frame.
      ResCon <- GraphAsDataFrame[GraphAsDataFrame$weight > 0,]
      ConRes <- GraphAsDataFrame[GraphAsDataFrame$weight < 0,]
      
      # Reorder and rename variables.
      ResCon <- dplyr::select(ResCon, 
                                 to, from, # resource = to, consumer = from, 
                                 effectPerUnit = weight, resourceAbund = N)
      ConRes <- dplyr::select(ConRes, 
                                 to, from, # resource = from, consumer = to, 
                                 effectPerUnit = weight, consumerAbund = N)
      ResCon <- dplyr::mutate(dplyr::group_by(ResCon, from),
                              effectActual = effectPerUnit * resourceAbund,
                              Type = "Exploit+")
      ConRes <- dplyr::mutate(dplyr::group_by(ConRes, from),
                              effectActual = effectPerUnit * consumerAbund,
                              Type = ifelse(from == to,
                                            "SelfReg-",
                                            "Exploit-"))
      
      IntriG <- with(redPool, data.frame(
                              from = node, #resource = node,
                              to = node, #consumer = node,
                              effectPerUnit = ifelse(ReproductionRate > 0,
                                                   ReproductionRate, 0),
                              effectActual = ifelse(ReproductionRate > 0,
                                                  N * ReproductionRate, 0),
                              Type = "Intrisc+")) 
      IntriL <- with(redPool, data.frame(
                              from = node, #resource = node,
                              to = node, #consumer = node,
                              effectPerUnit = ifelse(ReproductionRate < 0,
                                                   ReproductionRate, 0),
                              effectActual = ifelse(ReproductionRate < 0,
                                                  N * ReproductionRate, 0),
                              Type = "Intrisc-"))
      
      EdgeDataFrame <- dplyr::bind_rows(
        dplyr::select(ResCon, -resourceAbund), 
        dplyr::select(ConRes, -consumerAbund),
        IntriG, IntriL
      )
      
      EdgeDataFrame <- EdgeDataFrame %>% dplyr::rename(
        # Empirically speaking, to and from appear reversed.
        # A consumer (from) should have a negative effect on resource (to),
        # but the organisation so far marks it as positive. We fix this.
        tempname = to,
        to = from
      ) %>% dplyr::rename(
        from = tempname
      ) %>% dplyr::filter(
        # Remove placeholder entries
        effectPerUnit != 0
      ) %>% dplyr::mutate(
        # Useful to keep effects separate
        effectSign = sign(effectPerUnit)
      ) %>% group_by(
        to, effectSign
      ) %>% dplyr::mutate(
        # Perform the post mortem of the most influential from's
        effectEfficiency = effectPerUnit / sum(effectPerUnit), 
        effectNormalised = effectActual / sum(effectActual)
      ) %>% dplyr::arrange(to)
      
      list(
        Edges = EdgeDataFrame,
        Vertices = redPool
      )
    }
  )
}

Preparatory code:

toCheddar <- function(EVList, name = "") {# Edges Vertices List
  links <- EVList$Edges

  # cheddar does not like "cannibalism".
  links <- links[
    links$to != links$from,
  ]

  # "[C]olumns called ‘resource’ and ‘consumer’ must be given."
  links <- dplyr::bind_rows(
    links %>% dplyr::filter(effectSign == 1) %>% dplyr::rename(
      resource = from, consumer = to),
    links %>% dplyr::filter(effectSign == -1) %>% dplyr::rename(
      resource = to, consumer = from),
  ) %>% dplyr::select(-Type) # Cheddar confuses node Type and edge Type.

  cheddar::Community(
    nodes = EVList$Vertices,
    properties = list(
      title = name,
      M.units = "masses",
      N.units = "abund"
    ),
    trophic.links = links
  )
}

toIGraph <- function(EVList, sign = 0) {
  igraph::graph_from_data_frame(
    d = if(sign == 0) {
      EVList$Edges
    } else {
      EVList$Edges[EVList$Edges$effectSign == sign, ]
    },
    directed = TRUE,
    vertices = EVList$Vertices
  )
}

toPostMortem <- function(EVList,
                         threshold = 0, # sets to minimal size edges below
                         nodeSize = c("None", "Abundance", "Size"),
                         edgeScale = 10,
                         reducedTrophic = TRUE) {
  if (tolower(threshold) == "adaptive") {
    threshold = EVList$Edges %>% group_by(
      to, effectSign
    ) %>% summarise(
      max = max(effectNormalised), .groups = "drop"
    ) %>% ungroup %>% pull(max) %>% min
  }

  theGc <- toCheddar(EVList, name = "Trophic Levels")
  theGi <- toIGraph(EVList)

  theGiGain <- toIGraph(EVList, sign = 1)
  theGiLoss <- toIGraph(EVList, sign = -1)

  theLayout <- igraph::layout.circle(theGi)

  theSize <- match.arg(nodeSize, c("Abundance", "Size", "None"))
  if (theSize == "Abundance")
    theVs <- sqrt(igraph::vertex_attr(theGi)$N) * 10
  else if (theSize == "Size") {
    theVs <- igraph::vertex_attr(theGi)$M
    theVs <- sqrt(theVs / min(theVs)) * 10
  } else if (theSize == "None") {
    theVs <- 15
  }

  theColors <- ifelse(
    igraph::vertex_attr(theGi)$Type == "Basal", "skyblue", "red"
  )

  theBoth <- igraph::edge_attr(theGi)$effectNormalised
  theGain <- igraph::edge_attr(theGiGain)$effectNormalised
  theLoss <- igraph::edge_attr(theGiLoss)$effectNormalised

  theBoth[theBoth < threshold] <- 0
  theGain[theGain < threshold] <- 0
  theLoss[theLoss < threshold] <- 0

  # Inform the graphs of which edges are not needed.
  theGi <- igraph::delete_edges(theGi, which(theBoth == 0))
  theGiGain <- igraph::delete_edges(theGiGain, which(theGain == 0))
  theGiLoss <- igraph::delete_edges(theGiLoss, which(theLoss == 0))

  # Remove the same entries so that lengths match.
  theGain <- theGain[theGain > 0]
  theLoss <- theLoss[theLoss > 0]

  theGain <- theGain * edgeScale
  theLoss <- theLoss * edgeScale

  parold <- par(no.readonly = TRUE)
  par(mfrow = c(2, 2), # Two Rows, Two Columns
      mar = c(0, 1.5, 1, 0), # Margins, bottom, left, top, right
      oma = c(0.1, 0.1, 0.1, 0.1) # Outer margins.
  )

  cheddar::PlotWebByLevel(
    theGc,
    show.level.lines = TRUE,
    level = "LongWeightedTrophicLevel"
  )

  if (!reducedTrophic) {
    plot(
      theGi,
      layout = theLayout,
      vertex.size = theVs,
      edge.width = 1,
      edge.arrow.size = 0.3,
      edge.arrow.width = 1,
      vertex.color = theColors,
      edge.lty = 2,
      edge.color = "grey",
      edge.arrow.mode = ">",
      main = "Consumption"
    )
  } else {
    EVListRed <- EVList
    EVListRed$Edges <- EVListRed$Edges %>% dplyr::filter(
      effectNormalised >= threshold
    )
    theGc2 <- toCheddar(EVListRed, name = "Strongest Trophic Levels")
    cheddar::PlotWebByLevel(
      theGc2,
      show.level.lines = TRUE,
      level = "LongWeightedTrophicLevel"
    )
  }

  plot(
    theGiGain,
    layout = theLayout,
    vertex.size = theVs,
    edge.width = theGain,
    edge.arrow.size = 0.3,
    edge.arrow.width = 1,
    vertex.color = theColors,
    edge.lty = 2,
    edge.color = "blue",
    edge.arrow.mode = ">",
    main = "Consumer's Gains"
  )

  plot(
    theGiLoss,
    layout = theLayout,
    vertex.size = theVs,
    edge.width = theLoss,
    edge.arrow.size = 0.3,
    edge.arrow.width = 2,
    vertex.color = theColors,
    edge.lty = 3,
    edge.color = "darkred",
    edge.arrow.mode = "<",
    main = "Resource's Losses"
  )
  
  par(parold)
  
  EVList$Edges %>% dplyr::ungroup() %>% dplyr::filter(
    effectNormalised >= threshold
  ) %>% dplyr::select(
    -effectSign
  ) %>% dplyr::arrange(
    to, -effectNormalised
  )
}
thresholdEdges <- 0.3

We use a threshold of 0.3 at first, followed by an adaptive threshold. For the adaptive, we use the smallest largest effect of a given type for a given recipient. To break that down, the largest effect of a given type might be used as a proxy for how specialist a given recipient’s interactions are. The smallest one of these can be thought of as the most generalist species in the graph’s threshold to have at least one edge of both positive and negative type included.

Graph 1

toPostMortem(foodWebs[[1]], nodeSize = "None", threshold = thresholdEdges) -> temp

temp

Graph 5

toPostMortem(foodWebs[[5]], nodeSize = "None", threshold = thresholdEdges) -> temp

temp

Graph 9

toPostMortem(foodWebs[[9]], nodeSize = "None", threshold = thresholdEdges) -> temp

temp

Graph 1 Adaptive

toPostMortem(foodWebs[[1]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

Graph 5 Adaptive

toPostMortem(foodWebs[[5]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

Graph 9 Adaptive

toPostMortem(foodWebs[[9]], nodeSize = "None", threshold = "Adaptive") -> temp

temp
LS0tDQp0aXRsZTogIkxvb2tpbmcgYXQgQ29ubmVjdGFuY2UiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQotLS0NCg0KYGBge3IgbGlic30NCiMgQ2hlY2sgcmVxdWlzaXRlIHBhY2thZ2VzIGFyZSBpbnN0YWxsZWQuDQpwYWNrYWdlcyA8LSBjKA0KICAicGxvdGx5IiwNCiAgImRwbHlyIg0KKQ0KZm9yIChwa2cgaW4gcGFja2FnZXMpIHsNCiAgbGlicmFyeShwa2csIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCn0NCmBgYA0KDQojIExvYWQNClB1bGxpbmcgY29kZSBhbG1vc3QgZGlyZWN0bHkgZnJvbSBgTE0xOTk2LU51bVBvb2xDb20tUURhdE1ha2UtMjAyMS0wNS5SbWRgLg0KYGBge3IgZGlyc30NCmRpclZpa2luZyA8LSBjKA0KICBmaWxlLnBhdGgoDQogICAgZ2V0d2QoKSwgIkxDQUJfTGF3TW9ydG9uMTk5Ni1OdW1lcmljYWxQb29sQ29tbXVuaXR5U2NhbGluZzUiDQogICkNCikNCmRpclZpa2luZ1Jlc3VsdHMgPC0gZmlsZS5wYXRoKA0KICBkaXJWaWtpbmcsIHBhc3RlMCgNCiAgICAic2F2ZS0iLCBjKA0KICAgICAgIjIwMjEtMDUtMTkiLA0KICAgICAgIjIwMjEtMDUtMjEiLA0KICAgICAgIjIwMjEtMDUtMjQiDQogICAgKQ0KICApDQopDQpyZXN1bHRGb3JtYXQgPC0gcGFzdGUwKA0KICAicnVuLSIsDQogICIlZCIsICMgQ29tYmluYXRpb24gTnVtYmVyLCBvciBDb21ibk51bS4NCiAgIi0iLA0KICAiJXMiLCAjIFJ1biBTZWVkLg0KICAiLlJEUyINCikNCmBgYA0KDQojIyBEYXRhDQpgYGB7ciBvcmdhbmlzZVBhcmFtczJ9DQpzb3VyY2UoDQogIGZpbGUucGF0aChnZXR3ZCgpLA0KICAgICAgICAgICAgIkxhd01vcnRvbjE5OTYtTnVtZXJpY2FsUG9vbENvbW11bml0eVNjYWxpbmctU2V0dGluZ3M1LlIiKQ0KKQ0KDQpwYXJhbUZyYW1lIDwtIHdpdGgobGlzdCgNCiAgYiA9IHJlcChiYXNhbCwgdGltZXMgPSBsZW5ndGgoY29uc3VtZXIpKSwNCiAgYyA9IHJlcChjb25zdW1lciwgZWFjaCA9IGxlbmd0aChiYXNhbCkpLA0KICBzMSA9IHNlZWRzUHJlcFsxOihsZW5ndGgoYmFzYWwpICogbGVuZ3RoKGNvbnN1bWVyKSldLA0KICBzMiA9IHNlZWRzUHJlcFsNCiAgICAobGVuZ3RoKGJhc2FsKSAqIGxlbmd0aChjb25zdW1lcikgKyAxKTooDQogICAgICAyICogbGVuZ3RoKGJhc2FsKSAqIGxlbmd0aChjb25zdW1lcikpDQogIF0sDQogIHNSID0gc2VlZHNSdW4NCiksIHsNCiAgdGVtcCA8LSBkYXRhLmZyYW1lKA0KICAgIENvbWJuTnVtID0gMCwNCiAgICBCYXNhbHMgPSBiLA0KICAgIENvbnN1bWVycyA9IGMsDQogICAgU2VlZFBvb2wgPSBzMSwNCiAgICBTZWVkTWF0ID0gczIsDQogICAgU2VlZFJ1bnMgPSAiIiwNCiAgICBTZWVkUnVuc051bSA9IDAsDQogICAgRW5kU3RhdGVzID0gSShyZXAobGlzdCgiIiksIGxlbmd0aChiKSkpLA0KICAgIEVuZFN0YXRlc051bSA9IDAsDQogICAgRW5kU3RhdGVTaXplcyA9IEkocmVwKGxpc3QoIiIpLCBsZW5ndGgoYikpKSwNCiAgICBFbmRTdGF0ZVNpemVzTnVtID0gTkEsDQogICAgRW5kU3RhdGVBc3NlbWJseSA9IEkocmVwKGxpc3QoIiIpLCBsZW5ndGgoYikpKSwNCiAgICBFbmRTdGF0ZUFidW5kYW5jZSA9IEkocmVwKGxpc3QoIiIpLCBsZW5ndGgoYikpKSwNCiAgICBEYXRhc2V0ID0gIjIwMjEtMDU6NTpDb25uZWN0YW5jZSIsDQogICAgRGF0YXNldElEID0gNSwNCiAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UNCiAgKQ0KICBmb3IgKGkgaW4gMTpucm93KHRlbXApKSB7DQogICAgc2VlZHMgPC0gc1JbKChpIC0gMSkgKiBydW5zICsgMSkgOiAoaSAqIHJ1bnMpXQ0KICAgIHRlbXAkU2VlZFJ1bnNbaV0gPC0gdG9TdHJpbmcoc2VlZHMpICMgQ1NWDQogICAgdGVtcCRTZWVkUnVuc051bVtpXSA8LSBsZW5ndGgoc2VlZHMpDQogIH0NCiAgdGVtcCRDb21ibk51bSA8LSAxOm5yb3codGVtcCkNCiAgdGVtcA0KfSkNCmBgYA0KDQpgYGB7ciBsb2FkUmVzdWx0czJ9DQojIE5vdGU6IG4gKyAyIGVuZCBzdGF0ZXMuIEZhaWx1cmUgdG8gZmluaXNoLCBmYWlsdXJlIHRvIG9idGFpbiBzdGF0ZSwgYW5kIHN0YXRlLg0KIyBNb2RpZmllZCBmcm9tIGFib3ZlLCBidXQgd2l0aCB0aGUgYWJ1bmRhbmNlIHJlY29yZGVkLg0KZm9yIChpIGluIDE6bnJvdyhwYXJhbUZyYW1lKSkgew0KICByZXN1bHRzTGlzdCA8LSBsaXN0KA0KICAgICJObyBSdW4iID0gMCwNCiAgICAiTm8gU3RhdGUiID0gMA0KICApDQogIHJlc3VsdHNTaXplIDwtIGxpc3QoDQogICAgIjAiID0gMA0KICApDQogIHJlc3VsdHNBc3NlbWJseSA8LSBsaXN0KA0KICAgICJObyBSdW4iID0gZGF0YS5mcmFtZSgpLA0KICAgICJObyBTdGF0ZSIgPSBkYXRhLmZyYW1lKCkNCiAgKQ0KICByZXN1bHRzQWJ1bmQgPC0gbGlzdCgNCiAgICAiTm8gUnVuIiA9ICIiLA0KICAgICJObyBTdGF0ZSIgPSAiIg0KICApDQogIHNlZWRzIDwtIHVubGlzdChzdHJzcGxpdChwYXJhbUZyYW1lJFNlZWRSdW5zW2ldLCAnLCAnKSkNCiAgZm9yIChzZWVkIGluIHNlZWRzKSB7DQogICAgZmlsZU5hbWUgPC0gZmlsZS5wYXRoKA0KICAgICAgZGlyVmlraW5nUmVzdWx0cywNCiAgICAgIHNwcmludGYocmVzdWx0Rm9ybWF0LCBwYXJhbUZyYW1lJENvbWJuTnVtW2ldLCBzZWVkKQ0KICAgICkNCg0KICAgIGZpbGVOYW1lIDwtIGZpbGVOYW1lW2ZpbGUuZXhpc3RzKGZpbGVOYW1lKV0NCiAgICANCiAgICBpZiAobGVuZ3RoKGZpbGVOYW1lKSA+PSAxKSB7DQogICAgICBpZiAobGVuZ3RoKGZpbGVOYW1lKSA9PSAyKSB7DQogICAgICAgIHRlbXAgPC0gbG9hZChmaWxlTmFtZVsxXSkNCiAgICAgICAgdGVtcCA8LSBldmFsKHBhcnNlKHRleHQgPSB0ZW1wKSkNCiAgICAgICAgdGVtcDIgPC0gbG9hZChmaWxlTmFtZVsyXSkNCiAgICAgICAgdGVtcDIgPC0gZXZhbChwYXJzZSh0ZXh0ID0gdGVtcDIpKQ0KICAgICAgICBpZiAoIWlkZW50aWNhbCh0ZW1wLCB0ZW1wMikpIHsNCiAgICAgICAgICBzdG9wKCIyIGZpbGVzLCBidXQgbm90IGlkZW50aWNhbC4iKQ0KICAgICAgICB9DQogICAgICB9IGVsc2UgaWYgKGxlbmd0aChmaWxlTmFtZSkgPiAyKSB7DQogICAgICAgIHN0b3AoIkF0IGxlYXN0IDMgc2FtZSBmaWxlcy4iKQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgdGVtcCA8LSBsb2FkKGZpbGVOYW1lKQ0KICAgICAgICB0ZW1wIDwtIGV2YWwocGFyc2UodGV4dCA9IHRlbXApKSAjIEdldCBvYmplY3RzLg0KICAgICAgfQ0KDQogICAgICBpZiAoaXMubGlzdCh0ZW1wKSAmJiAiUmVzdWx0IiAlaW4lIG5hbWVzKHRlbXApKSB7DQoNCiAgICAgICAgaWYgKGlzLmRhdGEuZnJhbWUodGVtcCRSZXN1bHQpKQ0KICAgICAgICAgIGNvbW11bml0eSA8LSB0ZW1wJFJlc3VsdCRDb21tdW5pdHlbW25yb3codGVtcCRSZXN1bHQpXV0NCiAgICAgICAgZWxzZQ0KICAgICAgICAgIGNvbW11bml0eSA8LSB0ZW1wJFJlc3VsdA0KDQogICAgICAgIHNpemUgPC0gdG9TdHJpbmcobGVuZ3RoKGNvbW11bml0eSkpDQoNCiAgICAgICAgaWYgKGNvbW11bml0eVsxXSAhPSAiIikNCiAgICAgICAgICBhYnVuZCA8LSB0b1N0cmluZyh0ZW1wJEFidW5kW2NvbW11bml0eSArIDFdKQ0KICAgICAgICBlbHNlDQogICAgICAgICAgYWJ1bmQgPC0gIiINCg0KICAgICAgICBjb21tdW5pdHkgPC0gdG9TdHJpbmcoY29tbXVuaXR5KQ0KDQogICAgICAgIGlmIChjb21tdW5pdHkgPT0gIiIpIHsNCiAgICAgICAgICByZXN1bHRzTGlzdCRgTm8gU3RhdGVgIDwtIHJlc3VsdHNMaXN0JGBObyBTdGF0ZWAgKyAxDQogICAgICAgICAgcmVzdWx0c1NpemUkYDBgIDwtIHJlc3VsdHNTaXplJGAwYCArIDENCg0KICAgICAgICB9IGVsc2UgaWYgKGNvbW11bml0eSAlaW4lIG5hbWVzKHJlc3VsdHNMaXN0KSkgew0KICAgICAgICAgIHJlc3VsdHNMaXN0W1tjb21tdW5pdHldXSA8LSByZXN1bHRzTGlzdFtbY29tbXVuaXR5XV0gKyAxDQogICAgICAgICAgcmVzdWx0c1NpemVbW3NpemVdXSA8LSByZXN1bHRzU2l6ZVtbc2l6ZV1dICsgMQ0KDQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgcmVzdWx0c0xpc3RbW2NvbW11bml0eV1dIDwtIDENCiAgICAgICAgICByZXN1bHRzQXNzZW1ibHlbW2NvbW11bml0eV1dIDwtIHRlbXANCiAgICAgICAgICByZXN1bHRzQWJ1bmRbW2NvbW11bml0eV1dIDwtIGFidW5kDQoNCiAgICAgICAgICBpZiAoc2l6ZSAlaW4lIHJlc3VsdHNTaXplKSB7DQogICAgICAgICAgICByZXN1bHRzU2l6ZVtbc2l6ZV1dIDwtIHJlc3VsdHNTaXplW1tzaXplXV0gKyAxDQogICAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAgIHJlc3VsdHNTaXplW1tzaXplXV0gPC0gMQ0KICAgICAgICAgIH0NCiAgICAgICAgfQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgcmVzdWx0c0xpc3QkYE5vIFN0YXRlYCA8LSByZXN1bHRzTGlzdCRgTm8gU3RhdGVgICsgMQ0KICAgICAgICByZXN1bHRzU2l6ZSRgMGAgPC0gcmVzdWx0c1NpemUkYDBgICsgMQ0KICAgICAgfQ0KICAgIH0gZWxzZSB7DQogICAgICByZXN1bHRzTGlzdCRgTm8gUnVuYCA8LSByZXN1bHRzTGlzdCRgTm8gUnVuYCArIDENCiAgICAgIHJlc3VsdHNTaXplJGAwYCA8LSByZXN1bHRzU2l6ZSRgMGAgKyAxDQogICAgfQ0KICB9DQoNCiAgcGFyYW1GcmFtZSRFbmRTdGF0ZXNbW2ldXSA8LSByZXN1bHRzTGlzdA0KICBwYXJhbUZyYW1lJEVuZFN0YXRlc051bVtpXSA8LSBsZW5ndGgocmVzdWx0c0xpc3QpIC0gMiAjICEgTm8gU3RhdGUsIE5vIFJ1bg0KICBwYXJhbUZyYW1lJEVuZFN0YXRlU2l6ZXNbW2ldXSA8LSByZXN1bHRzU2l6ZQ0KICBwYXJhbUZyYW1lJEVuZFN0YXRlU2l6ZXNOdW1baV0gPC0gbGVuZ3RoKHJlc3VsdHNTaXplKSAtIDEgIyAhIDANCiAgcGFyYW1GcmFtZSRFbmRTdGF0ZUFzc2VtYmx5W1tpXV0gPC0gcmVzdWx0c0Fzc2VtYmx5DQogIHBhcmFtRnJhbWUkRW5kU3RhdGVBYnVuZGFuY2VbW2ldXSA8LSByZXN1bHRzQWJ1bmQNCn0NCmBgYA0KDQojIyBQbG90DQoNCmBgYHtyIHBsb3QzRH0NCiMgWCwgWSwgQmFzYWwgYW5kIENvbnN1bWVyLg0KIyBaID0gU2l6ZXMgb2YgdGhlIEVuZHN0YXRlcy4NCg0KcGxvdFNjYWxpbmdEYXRhIDwtIGRhdGEuZnJhbWUoDQogIENvbWJuTnVtID0gcmVwKHBhcmFtRnJhbWUkQ29tYm5OdW0sIHBhcmFtRnJhbWUkRW5kU3RhdGVzTnVtKSwNCiAgQmFzYWxzID0gcmVwKHBhcmFtRnJhbWUkQmFzYWxzLCBwYXJhbUZyYW1lJEVuZFN0YXRlc051bSksDQogIENvbnN1bWVycyA9IHJlcChwYXJhbUZyYW1lJENvbnN1bWVycywgcGFyYW1GcmFtZSRFbmRTdGF0ZXNOdW0pLA0KICBEYXRhc2V0ID0gcmVwKHBhcmFtRnJhbWUkRGF0YXNldCwgcGFyYW1GcmFtZSRFbmRTdGF0ZXNOdW0pLA0KICBEYXRhc2V0SUQgPSByZXAocGFyYW1GcmFtZSREYXRhc2V0SUQsIHBhcmFtRnJhbWUkRW5kU3RhdGVzTnVtKQ0KKQ0KDQojIENvbW11bml0aWVzDQpjb21tcyA8LSB1bmxpc3QobGFwcGx5KHBhcmFtRnJhbWUkRW5kU3RhdGVzLCBuYW1lcykpDQpmcmVxcyA8LSB1bmxpc3QocGFyYW1GcmFtZSRFbmRTdGF0ZXMpDQphc21ibCA8LSB1bmxpc3QocGFyYW1GcmFtZSRFbmRTdGF0ZUFzc2VtYmx5LCByZWN1cnNpdmUgPSBGQUxTRSkNCmFzbWJsIDwtIGFzbWJsW2NvbW1zICE9ICJObyBSdW4iICYgY29tbXMgIT0gIk5vIFN0YXRlIl0NCmZyZXFzIDwtIGZyZXFzW2NvbW1zICE9ICJObyBSdW4iICYgY29tbXMgIT0gIk5vIFN0YXRlIl0NCmNvbW1zIDwtIGNvbW1zW2NvbW1zICE9ICJObyBSdW4iICYgY29tbXMgIT0gIk5vIFN0YXRlIl0NCg0KYXNtYmwgPC0gbGFwcGx5KGFzbWJsLCBmdW5jdGlvbihkKSB7DQogIGlmIChpcy5udWxsKGQpKSByZXR1cm4oTkEpDQogIGlmICgiUmVzdWx0Lk91dGNvbWUiICVpbiUgbmFtZXMoZCkpDQogICAgZCAlPiUgZHBseXI6OmZpbHRlcihSZXN1bHQuT3V0Y29tZSAhPSAiVHlwZSAxIChGYWlsdXJlKSIgJg0KICAgICAgICAgICAgICAgICAgICAgICAgICBSZXN1bHQuT3V0Y29tZSAhPSAiUHJlc2VudCIpDQogIGVsc2UNCiAgICBkJFJlc3VsdCAlPiUgZHBseXI6OmZpbHRlcihPdXRjb21lICE9ICJUeXBlIDEgKEZhaWx1cmUpIiAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPdXRjb21lICE9ICJQcmVzZW50IikNCn0pDQoNCnBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdGllcyA8LSBjb21tcw0KcGxvdFNjYWxpbmdEYXRhJENvbW11bml0eUZyZXEgPC0gZnJlcXMNCnBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdHlTZXEgPC0gYXNtYmwNCg0KIyBDb21tdW5pdHkgU2l6ZQ0KdGVtcCA8LSB1bmxpc3QobGFwcGx5KHN0cnNwbGl0KHBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdGllcywgJywnKSwgbGVuZ3RoKSkNCnBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdHlTaXplIDwtIHRlbXANCg0KIyBGb3IgdXNhZ2UgYnkgdGhlIHJlYWRlci4NCg0KcGxvdFNjYWxpbmcgPC0gcGxvdGx5OjpwbG90X2x5KA0KICBwbG90U2NhbGluZ0RhdGEsDQogIHggPSB+QmFzYWxzLA0KICB5ID0gfkNvbnN1bWVycywNCiAgeiA9IH5Db21tdW5pdHlTaXplLA0KICBjb2xvciA9IH5EYXRhc2V0LA0KICBjb2xvcnMgPSBjKCJyZWQiLCAiYmx1ZSIsICJibGFjayIpDQopDQoNCnBsb3RTY2FsaW5nIDwtIHBsb3RseTo6YWRkX21hcmtlcnMocGxvdFNjYWxpbmcpDQoNCnBsb3RTY2FsaW5nIDwtIHBsb3RseTo6bGF5b3V0KA0KICBwbG90U2NhbGluZywNCiAgc2NlbmUgPSBsaXN0KA0KICAgIHhheGlzID0gbGlzdCh0eXBlID0gImxvZyIpLA0KICAgIHlheGlzID0gbGlzdCh0eXBlID0gImxvZyIpLA0KICAgIGNhbWVyYSA9IGxpc3QoDQogICAgICBleWUgPSBsaXN0KA0KICAgICAgICB4ID0gLTEuMjUsIHkgPSAtMS4yNSwgeiA9IC4wNQ0KICAgICAgKQ0KICAgICkNCiAgKQ0KKQ0KDQpwbG90U2NhbGluZw0KYGBgDQoNCiMjIEFidW5kYW5jZXMNCg0KYGBge3IgbG9hZFBvb2xzTWF0c30NCiMgPiBydW5pZigxKSAqIDFFOA0KIyBbMV0gODI1OTg2NzkNCnNldC5zZWVkKDgyNTk4Njc5KQ0KDQptYXRzIDwtIGxpc3QoKQ0KcG9vbHNhbGwgPC0gbGlzdCgpICMgbmFtZSBwb29scyB1c2VkIGluIHNhdmUgZGF0YTsgYmUgY2FyZWZ1bCENCg0KZm9yIChpIGluIDE6bGVuZ3RoKGRpclZpa2luZykpIHsNCiAgdGVtcCA8LSBsb2FkKGZpbGUucGF0aCgNCiAgICBkaXJWaWtpbmdbaV0sDQogICAgcGFzdGUwKCJMYXdNb3J0b24xOTk2LU51bWVyaWNhbFBvb2xDb21tdW5pdHlTY2FsaW5nLVBvb2xNYXRzIiwNCiAgICAgICAgICAgNSwgI2lmIChpID4gMSkgaSBlbHNlICIiLA0KICAgICAgICAgICAiLlJEUyIpDQogICkpDQogIG1hdHNbW2ldXSA8LSBldmFsKHBhcnNlKHRleHQgPSB0ZW1wWzFdKSkNCiAgcG9vbHNhbGxbW2ldXSA8LSBldmFsKHBhcnNlKHRleHQgPSB0ZW1wWzJdKSkNCn0NCnBvb2xzIDwtIHBvb2xzYWxsDQpgYGANCg0KYGBge3IgY29tcHV0ZUNhbmRpZGF0ZXN9DQpjYW5kaWRhdGVEYXRhIDwtIHBsb3RTY2FsaW5nRGF0YSAlPiUgZHBseXI6Omdyb3VwX2J5KA0KICBDb21ibk51bSwgRGF0YXNldA0KKSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgT3RoZXJTdGVhZHlTdGF0ZXMgPSBkcGx5cjo6bigpIC0gMQ0KKSAlPiUgZHBseXI6OmZpbHRlcigNCiAgT3RoZXJTdGVhZHlTdGF0ZXMgPiAwDQopDQpjYW5kaWRhdGVEYXRhICU+JSBkcGx5cjo6c2VsZWN0KC1Db21tdW5pdHlTZXEpDQpgYGANCg0KQWxhcywgdGhlcmUgYXJlIGByIG5yb3coY2FuZGlkYXRlRGF0YSAlPiUgZHBseXI6OmZpbHRlcihPdGhlclN0ZWFkeVN0YXRlcyA+IDApKWAgcm93cy4NClNvIHdlIGFyZSBkb25lLCBidXQgcGVyaGFwcyB3ZSBjYW4gY2xvc2UgdGhpcyBmaWxlIHdpdGggc29tZSBsaXR0bGUgdGhvdWdodHMgYW5kIGNvbW1lbnRzLg0KRm9yIGluc3RhbmNlLCB3ZSBjYW4gcmVjeWNsZSBzb21lIGVhcmxpZXIgY29kZSB0byBsb29rIGF0IHRoZSB0d28gbGFyZ2VzdCBjb21tdW5pdGllcyB0byBzZWUgaWYgdGhlcmUgaXMgYW55dGhpbmcgaW50ZXJlc3RpbmcgZ29pbmcgb24uDQpXZSdsbCBsb2FkIHRoZSBhYnVuZGFuY2VzIGFueXdheXMuDQoNCmBgYHtyIGNvbXB1dGVDYW5kaWRhdGVzQW55d2F5c30NCmNhbmRpZGF0ZURhdGEgPC0gcGxvdFNjYWxpbmdEYXRhICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogIENvbWJuTnVtLCBEYXRhc2V0DQopICU+JSBkcGx5cjo6bXV0YXRlKA0KICBPdGhlclN0ZWFkeVN0YXRlcyA9IGRwbHlyOjpuKCkgLSAxDQopDQpgYGANCg0KYGBge3IgbG9hZEFidW5kYW5jZXN9DQojIEZpcnN0LCBjaGVjayBpZiBpdCBpcyBpbiB0aGUgcGFyYW1GcmFtZS4NCiMgU2Vjb25kLCBjaGVjayBpZiBpdCBpcyBpbiB0aGUgc2F2ZWQgZGF0YSBmcm9tIHRoZSBwcmV2aW91cy4NCiMgT3RoZXJ3aXNlLCBpZ25vcmUgaXQsIHdlJ2xsIGZpZ3VyZSBvdXQgd2hhdCBpdCBpcyBhbmQgd2h5IGl0IGlzIG1pc3NpbmcgbGF0ZXIuDQoNCmNhbmRpZGF0ZURhdGEkQ29tbXVuaXR5QWJ1bmQgPC0gIiINCg0KZm9yIChyIGluIDE6bnJvdyhjYW5kaWRhdGVEYXRhKSkgew0KICAjIElEIDE6NCBhcmUgdXNlZCB0byBpZGVudGlmeSBwYXJhbUZyYW1lLCA1IHVzZWQgdG8gaWRlbnRpZnkgYWJ1bmRhbmNlDQogIElEIDwtIGNhbmRpZGF0ZURhdGFbciwgMTo2XQ0KICBwYXJhbUZyYW1lUm93IDwtIHBhcmFtRnJhbWUgJT4lIGRwbHlyOjpmaWx0ZXIoDQogICAgQ29tYm5OdW0gPT0gSUQkQ29tYm5OdW0sDQogICAgQmFzYWxzID09IElEJEJhc2FscywNCiAgICBDb25zdW1lcnMgPT0gSUQkQ29uc3VtZXJzLA0KICAgIERhdGFzZXQgPT0gSUQkRGF0YXNldA0KICApDQoNCiAgaWYgKGlzLmxpc3QocGFyYW1GcmFtZVJvdyRFbmRTdGF0ZUFidW5kYW5jZVtbMV1dKSkgew0KICAgIGVudHJ5IDwtIHdoaWNoKElEJENvbW11bml0aWVzID09IG5hbWVzKHBhcmFtRnJhbWVSb3ckRW5kU3RhdGVBYnVuZGFuY2VbWzFdXSkpDQogICAgaWYgKGxlbmd0aChlbnRyeSkpIHsNCiAgICAgIGNhbmRpZGF0ZURhdGEkQ29tbXVuaXR5QWJ1bmRbcl0gPC0gcGFyYW1GcmFtZVJvdyRFbmRTdGF0ZUFidW5kYW5jZVtbMV1dW1tlbnRyeV1dDQogICAgICBuZXh0KCkNCiAgICB9DQogIH0NCn0NCmBgYA0KDQpgYGB7ciBmaWx0ZXJOb0FidW5kfQ0KY2FuZGlkYXRlRGF0YSA8LSBjYW5kaWRhdGVEYXRhICU+JSBkcGx5cjo6ZmlsdGVyKENvbW11bml0eUFidW5kICE9ICIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENvbW11bml0eUFidW5kICE9ICJGYWlsdXJlIikNCmBgYA0KDQpgYGB7ciBjb21wdXRlUHJvZHVjdGl2aXR5fQ0KY2FuZGlkYXRlRGF0YSRDb21tdW5pdHlQcm9kIDwtIE5BDQpmb3IgKHIgaW4gMTpucm93KGNhbmRpZGF0ZURhdGEpKSB7DQogIGNhbmRpZGF0ZURhdGEkQ29tbXVuaXR5UHJvZFtyXSA8LSB3aXRoKA0KICAgIGNhbmRpZGF0ZURhdGFbciwgXSwNCiAgICBSTVRSQ29kZTI6OlByb2R1Y3Rpdml0eSgNCiAgICAgIFBvb2wgPSBwb29sc1tbMV1dW1tDb21ibk51bV1dLA0KICAgICAgSW50ZXJhY3Rpb25NYXRyaXggPSBtYXRzW1sxXV1bW0NvbWJuTnVtXV0sDQogICAgICBDb21tdW5pdHkgPSBDb21tdW5pdGllcywNCiAgICAgIFBvcHVsYXRpb25zID0gQ29tbXVuaXR5QWJ1bmQNCiAgICApDQogICkNCn0NCmBgYA0KDQpgYGB7cn0NCmNhbmRpZGF0ZURhdGENCmBgYA0KIyMgR3JhcGggey50YWJzZXR9DQpUYWtpbmcgY29kZSBmcm9tIGBMTTE5OTYtTnVtUG9vbENvbS1Gb29kV2Vicy0yMDIxLTA3LlJtZGAuDQoNCmBgYHtyIGNyZWF0ZUdyYXBoc30NCmZvb2RXZWJzIDwtIGxpc3QoKQ0KDQpmb3IgKHIgaW4gMTpucm93KGNhbmRpZGF0ZURhdGEpKSB7DQogIGZvb2RXZWJzW1tyXV0gPC0gd2l0aCgNCiAgICBjYW5kaWRhdGVEYXRhW3IsIF0sDQogICAgew0KICAgICAgcmVkQ29tIDwtIFJNVFJDb2RlMjo6Q3N2Um93U3BsaXQoQ29tbXVuaXRpZXMpDQogICAgICByZWRNYXQgPC0gbWF0c1tbMV1dW1tDb21ibk51bV1dW3JlZENvbSwgcmVkQ29tXQ0KICAgICAgcmVkUG9vbCA8LSBwb29sc1tbMV1dW1tDb21ibk51bV1dW3JlZENvbSwgXQ0KICAgICAgDQogICAgICBjb2xuYW1lcyhyZWRNYXQpIDwtIHBhc3RlMCgncycsYXMuY2hhcmFjdGVyKHJlZENvbSkpDQogICAgICByb3duYW1lcyhyZWRNYXQpIDwtIGNvbG5hbWVzKHJlZE1hdCkNCiAgICAgIA0KICAgICAgbmFtZXMocmVkUG9vbClbMV0gPC0gIm5vZGUiDQogICAgICByZWRQb29sJG5vZGUgPC0gY29sbmFtZXMocmVkTWF0KQ0KICAgICAgbmFtZXMocmVkUG9vbClbM10gPC0gIk0iDQogICAgICANCiAgICAgIEdyYXBoIDwtIGlncmFwaDo6Z3JhcGhfZnJvbV9hZGphY2VuY3lfbWF0cml4KA0KICAgICAgICByZWRNYXQsIHdlaWdodGVkID0gVFJVRQ0KICAgICAgKQ0KICAgICAgDQogICAgICBHcmFwaCA8LSBpZ3JhcGg6OnNldC52ZXJ0ZXguYXR0cmlidXRlKA0KICAgICAgICBHcmFwaCwgIm5hbWUiLCB2YWx1ZSA9IGNvbG5hbWVzKHJlZE1hdCkNCiAgICAgICkNCiAgICAgIA0KICAgICAgcmVkUG9vbCROIDwtIFJNVFJDb2RlMjo6Q3N2Um93U3BsaXQoQ29tbXVuaXR5QWJ1bmQpDQogICAgICANCiAgICAgICMgRm9yIGxhdGVyIGFuYWx5c2lzLCB0YWtlIHRoZSBtYXRyaXggZGlhZ29uYWwuDQogICAgICANCiAgICAgIHJlZFBvb2wkSW50cmFzcGVjaWZpYyA8LSBkaWFnKHJlZE1hdCkNCiAgICAgIA0KICAgICAgR3JhcGhBc0RhdGFGcmFtZSA8LSBpZ3JhcGg6OmFzX2RhdGFfZnJhbWUoR3JhcGgpDQogIA0KICAgICAgIyBBZGQgaW4gYWJ1bmRhbmNlcyBmb3IgY2FsY3VsYXRpbmcgYWJ1bmRhbmNlICogKGdhaW4gb3IgbG9zcykNCiAgICAgIEdyYXBoQXNEYXRhRnJhbWUgPC0gZHBseXI6OmxlZnRfam9pbigNCiAgICAgICAgR3JhcGhBc0RhdGFGcmFtZSwNCiAgICAgICAgZHBseXI6OnNlbGVjdChyZWRQb29sLCBub2RlLCBOKSwNCiAgICAgICAgYnkgPSBjKCJ0byIgPSAibm9kZSIpDQogICAgICApDQogIA0KICAgICAgIyBTcGxpdCBkYXRhIGZyYW1lLg0KICAgICAgUmVzQ29uIDwtIEdyYXBoQXNEYXRhRnJhbWVbR3JhcGhBc0RhdGFGcmFtZSR3ZWlnaHQgPiAwLF0NCiAgICAgIENvblJlcyA8LSBHcmFwaEFzRGF0YUZyYW1lW0dyYXBoQXNEYXRhRnJhbWUkd2VpZ2h0IDwgMCxdDQogICAgICANCiAgICAgICMgUmVvcmRlciBhbmQgcmVuYW1lIHZhcmlhYmxlcy4NCiAgICAgIFJlc0NvbiA8LSBkcGx5cjo6c2VsZWN0KFJlc0NvbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0bywgZnJvbSwgIyByZXNvdXJjZSA9IHRvLCBjb25zdW1lciA9IGZyb20sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWZmZWN0UGVyVW5pdCA9IHdlaWdodCwgcmVzb3VyY2VBYnVuZCA9IE4pDQogICAgICBDb25SZXMgPC0gZHBseXI6OnNlbGVjdChDb25SZXMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG8sIGZyb20sICMgcmVzb3VyY2UgPSBmcm9tLCBjb25zdW1lciA9IHRvLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVmZmVjdFBlclVuaXQgPSB3ZWlnaHQsIGNvbnN1bWVyQWJ1bmQgPSBOKQ0KICAgICAgUmVzQ29uIDwtIGRwbHlyOjptdXRhdGUoZHBseXI6Omdyb3VwX2J5KFJlc0NvbiwgZnJvbSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZmZlY3RBY3R1YWwgPSBlZmZlY3RQZXJVbml0ICogcmVzb3VyY2VBYnVuZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFR5cGUgPSAiRXhwbG9pdCsiKQ0KICAgICAgQ29uUmVzIDwtIGRwbHlyOjptdXRhdGUoZHBseXI6Omdyb3VwX2J5KENvblJlcywgZnJvbSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZmZlY3RBY3R1YWwgPSBlZmZlY3RQZXJVbml0ICogY29uc3VtZXJBYnVuZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFR5cGUgPSBpZmVsc2UoZnJvbSA9PSB0bywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNlbGZSZWctIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkV4cGxvaXQtIikpDQogICAgICANCiAgICAgIEludHJpRyA8LSB3aXRoKHJlZFBvb2wsIGRhdGEuZnJhbWUoDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmcm9tID0gbm9kZSwgI3Jlc291cmNlID0gbm9kZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvID0gbm9kZSwgI2NvbnN1bWVyID0gbm9kZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVmZmVjdFBlclVuaXQgPSBpZmVsc2UoUmVwcm9kdWN0aW9uUmF0ZSA+IDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZXByb2R1Y3Rpb25SYXRlLCAwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVmZmVjdEFjdHVhbCA9IGlmZWxzZShSZXByb2R1Y3Rpb25SYXRlID4gMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTiAqIFJlcHJvZHVjdGlvblJhdGUsIDApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHlwZSA9ICJJbnRyaXNjKyIpKSANCiAgICAgIEludHJpTCA8LSB3aXRoKHJlZFBvb2wsIGRhdGEuZnJhbWUoDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmcm9tID0gbm9kZSwgI3Jlc291cmNlID0gbm9kZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvID0gbm9kZSwgI2NvbnN1bWVyID0gbm9kZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVmZmVjdFBlclVuaXQgPSBpZmVsc2UoUmVwcm9kdWN0aW9uUmF0ZSA8IDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZXByb2R1Y3Rpb25SYXRlLCAwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVmZmVjdEFjdHVhbCA9IGlmZWxzZShSZXByb2R1Y3Rpb25SYXRlIDwgMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTiAqIFJlcHJvZHVjdGlvblJhdGUsIDApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHlwZSA9ICJJbnRyaXNjLSIpKQ0KICAgICAgDQogICAgICBFZGdlRGF0YUZyYW1lIDwtIGRwbHlyOjpiaW5kX3Jvd3MoDQogICAgICAgIGRwbHlyOjpzZWxlY3QoUmVzQ29uLCAtcmVzb3VyY2VBYnVuZCksIA0KICAgICAgICBkcGx5cjo6c2VsZWN0KENvblJlcywgLWNvbnN1bWVyQWJ1bmQpLA0KICAgICAgICBJbnRyaUcsIEludHJpTA0KICAgICAgKQ0KICAgICAgDQogICAgICBFZGdlRGF0YUZyYW1lIDwtIEVkZ2VEYXRhRnJhbWUgJT4lIGRwbHlyOjpyZW5hbWUoDQogICAgICAgICMgRW1waXJpY2FsbHkgc3BlYWtpbmcsIHRvIGFuZCBmcm9tIGFwcGVhciByZXZlcnNlZC4NCiAgICAgICAgIyBBIGNvbnN1bWVyIChmcm9tKSBzaG91bGQgaGF2ZSBhIG5lZ2F0aXZlIGVmZmVjdCBvbiByZXNvdXJjZSAodG8pLA0KICAgICAgICAjIGJ1dCB0aGUgb3JnYW5pc2F0aW9uIHNvIGZhciBtYXJrcyBpdCBhcyBwb3NpdGl2ZS4gV2UgZml4IHRoaXMuDQogICAgICAgIHRlbXBuYW1lID0gdG8sDQogICAgICAgIHRvID0gZnJvbQ0KICAgICAgKSAlPiUgZHBseXI6OnJlbmFtZSgNCiAgICAgICAgZnJvbSA9IHRlbXBuYW1lDQogICAgICApICU+JSBkcGx5cjo6ZmlsdGVyKA0KICAgICAgICAjIFJlbW92ZSBwbGFjZWhvbGRlciBlbnRyaWVzDQogICAgICAgIGVmZmVjdFBlclVuaXQgIT0gMA0KICAgICAgKSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgICAgICAgIyBVc2VmdWwgdG8ga2VlcCBlZmZlY3RzIHNlcGFyYXRlDQogICAgICAgIGVmZmVjdFNpZ24gPSBzaWduKGVmZmVjdFBlclVuaXQpDQogICAgICApICU+JSBncm91cF9ieSgNCiAgICAgICAgdG8sIGVmZmVjdFNpZ24NCiAgICAgICkgJT4lIGRwbHlyOjptdXRhdGUoDQogICAgICAgICMgUGVyZm9ybSB0aGUgcG9zdCBtb3J0ZW0gb2YgdGhlIG1vc3QgaW5mbHVlbnRpYWwgZnJvbSdzDQogICAgICAgIGVmZmVjdEVmZmljaWVuY3kgPSBlZmZlY3RQZXJVbml0IC8gc3VtKGVmZmVjdFBlclVuaXQpLCANCiAgICAgICAgZWZmZWN0Tm9ybWFsaXNlZCA9IGVmZmVjdEFjdHVhbCAvIHN1bShlZmZlY3RBY3R1YWwpDQogICAgICApICU+JSBkcGx5cjo6YXJyYW5nZSh0bykNCiAgICAgIA0KICAgICAgbGlzdCgNCiAgICAgICAgRWRnZXMgPSBFZGdlRGF0YUZyYW1lLA0KICAgICAgICBWZXJ0aWNlcyA9IHJlZFBvb2wNCiAgICAgICkNCiAgICB9DQogICkNCn0NCmBgYA0KDQpQcmVwYXJhdG9yeSBjb2RlOg0KYGBge3IgZnVuY3Rpb25zfQ0KdG9DaGVkZGFyIDwtIGZ1bmN0aW9uKEVWTGlzdCwgbmFtZSA9ICIiKSB7IyBFZGdlcyBWZXJ0aWNlcyBMaXN0DQogIGxpbmtzIDwtIEVWTGlzdCRFZGdlcw0KDQogICMgY2hlZGRhciBkb2VzIG5vdCBsaWtlICJjYW5uaWJhbGlzbSIuDQogIGxpbmtzIDwtIGxpbmtzWw0KICAgIGxpbmtzJHRvICE9IGxpbmtzJGZyb20sDQogIF0NCg0KICAjICJbQ11vbHVtbnMgY2FsbGVkIOKAmHJlc291cmNl4oCZIGFuZCDigJhjb25zdW1lcuKAmSBtdXN0IGJlIGdpdmVuLiINCiAgbGlua3MgPC0gZHBseXI6OmJpbmRfcm93cygNCiAgICBsaW5rcyAlPiUgZHBseXI6OmZpbHRlcihlZmZlY3RTaWduID09IDEpICU+JSBkcGx5cjo6cmVuYW1lKA0KICAgICAgcmVzb3VyY2UgPSBmcm9tLCBjb25zdW1lciA9IHRvKSwNCiAgICBsaW5rcyAlPiUgZHBseXI6OmZpbHRlcihlZmZlY3RTaWduID09IC0xKSAlPiUgZHBseXI6OnJlbmFtZSgNCiAgICAgIHJlc291cmNlID0gdG8sIGNvbnN1bWVyID0gZnJvbSksDQogICkgJT4lIGRwbHlyOjpzZWxlY3QoLVR5cGUpICMgQ2hlZGRhciBjb25mdXNlcyBub2RlIFR5cGUgYW5kIGVkZ2UgVHlwZS4NCg0KICBjaGVkZGFyOjpDb21tdW5pdHkoDQogICAgbm9kZXMgPSBFVkxpc3QkVmVydGljZXMsDQogICAgcHJvcGVydGllcyA9IGxpc3QoDQogICAgICB0aXRsZSA9IG5hbWUsDQogICAgICBNLnVuaXRzID0gIm1hc3NlcyIsDQogICAgICBOLnVuaXRzID0gImFidW5kIg0KICAgICksDQogICAgdHJvcGhpYy5saW5rcyA9IGxpbmtzDQogICkNCn0NCg0KdG9JR3JhcGggPC0gZnVuY3Rpb24oRVZMaXN0LCBzaWduID0gMCkgew0KICBpZ3JhcGg6OmdyYXBoX2Zyb21fZGF0YV9mcmFtZSgNCiAgICBkID0gaWYoc2lnbiA9PSAwKSB7DQogICAgICBFVkxpc3QkRWRnZXMNCiAgICB9IGVsc2Ugew0KICAgICAgRVZMaXN0JEVkZ2VzW0VWTGlzdCRFZGdlcyRlZmZlY3RTaWduID09IHNpZ24sIF0NCiAgICB9LA0KICAgIGRpcmVjdGVkID0gVFJVRSwNCiAgICB2ZXJ0aWNlcyA9IEVWTGlzdCRWZXJ0aWNlcw0KICApDQp9DQoNCnRvUG9zdE1vcnRlbSA8LSBmdW5jdGlvbihFVkxpc3QsDQogICAgICAgICAgICAgICAgICAgICAgICAgdGhyZXNob2xkID0gMCwgIyBzZXRzIHRvIG1pbmltYWwgc2l6ZSBlZGdlcyBiZWxvdw0KICAgICAgICAgICAgICAgICAgICAgICAgIG5vZGVTaXplID0gYygiTm9uZSIsICJBYnVuZGFuY2UiLCAiU2l6ZSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGVkZ2VTY2FsZSA9IDEwLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjZWRUcm9waGljID0gVFJVRSkgew0KICBpZiAodG9sb3dlcih0aHJlc2hvbGQpID09ICJhZGFwdGl2ZSIpIHsNCiAgICB0aHJlc2hvbGQgPSBFVkxpc3QkRWRnZXMgJT4lIGdyb3VwX2J5KA0KICAgICAgdG8sIGVmZmVjdFNpZ24NCiAgICApICU+JSBzdW1tYXJpc2UoDQogICAgICBtYXggPSBtYXgoZWZmZWN0Tm9ybWFsaXNlZCksIC5ncm91cHMgPSAiZHJvcCINCiAgICApICU+JSB1bmdyb3VwICU+JSBwdWxsKG1heCkgJT4lIG1pbg0KICB9DQoNCiAgdGhlR2MgPC0gdG9DaGVkZGFyKEVWTGlzdCwgbmFtZSA9ICJUcm9waGljIExldmVscyIpDQogIHRoZUdpIDwtIHRvSUdyYXBoKEVWTGlzdCkNCg0KICB0aGVHaUdhaW4gPC0gdG9JR3JhcGgoRVZMaXN0LCBzaWduID0gMSkNCiAgdGhlR2lMb3NzIDwtIHRvSUdyYXBoKEVWTGlzdCwgc2lnbiA9IC0xKQ0KDQogIHRoZUxheW91dCA8LSBpZ3JhcGg6OmxheW91dC5jaXJjbGUodGhlR2kpDQoNCiAgdGhlU2l6ZSA8LSBtYXRjaC5hcmcobm9kZVNpemUsIGMoIkFidW5kYW5jZSIsICJTaXplIiwgIk5vbmUiKSkNCiAgaWYgKHRoZVNpemUgPT0gIkFidW5kYW5jZSIpDQogICAgdGhlVnMgPC0gc3FydChpZ3JhcGg6OnZlcnRleF9hdHRyKHRoZUdpKSROKSAqIDEwDQogIGVsc2UgaWYgKHRoZVNpemUgPT0gIlNpemUiKSB7DQogICAgdGhlVnMgPC0gaWdyYXBoOjp2ZXJ0ZXhfYXR0cih0aGVHaSkkTQ0KICAgIHRoZVZzIDwtIHNxcnQodGhlVnMgLyBtaW4odGhlVnMpKSAqIDEwDQogIH0gZWxzZSBpZiAodGhlU2l6ZSA9PSAiTm9uZSIpIHsNCiAgICB0aGVWcyA8LSAxNQ0KICB9DQoNCiAgdGhlQ29sb3JzIDwtIGlmZWxzZSgNCiAgICBpZ3JhcGg6OnZlcnRleF9hdHRyKHRoZUdpKSRUeXBlID09ICJCYXNhbCIsICJza3libHVlIiwgInJlZCINCiAgKQ0KDQogIHRoZUJvdGggPC0gaWdyYXBoOjplZGdlX2F0dHIodGhlR2kpJGVmZmVjdE5vcm1hbGlzZWQNCiAgdGhlR2FpbiA8LSBpZ3JhcGg6OmVkZ2VfYXR0cih0aGVHaUdhaW4pJGVmZmVjdE5vcm1hbGlzZWQNCiAgdGhlTG9zcyA8LSBpZ3JhcGg6OmVkZ2VfYXR0cih0aGVHaUxvc3MpJGVmZmVjdE5vcm1hbGlzZWQNCg0KICB0aGVCb3RoW3RoZUJvdGggPCB0aHJlc2hvbGRdIDwtIDANCiAgdGhlR2Fpblt0aGVHYWluIDwgdGhyZXNob2xkXSA8LSAwDQogIHRoZUxvc3NbdGhlTG9zcyA8IHRocmVzaG9sZF0gPC0gMA0KDQogICMgSW5mb3JtIHRoZSBncmFwaHMgb2Ygd2hpY2ggZWRnZXMgYXJlIG5vdCBuZWVkZWQuDQogIHRoZUdpIDwtIGlncmFwaDo6ZGVsZXRlX2VkZ2VzKHRoZUdpLCB3aGljaCh0aGVCb3RoID09IDApKQ0KICB0aGVHaUdhaW4gPC0gaWdyYXBoOjpkZWxldGVfZWRnZXModGhlR2lHYWluLCB3aGljaCh0aGVHYWluID09IDApKQ0KICB0aGVHaUxvc3MgPC0gaWdyYXBoOjpkZWxldGVfZWRnZXModGhlR2lMb3NzLCB3aGljaCh0aGVMb3NzID09IDApKQ0KDQogICMgUmVtb3ZlIHRoZSBzYW1lIGVudHJpZXMgc28gdGhhdCBsZW5ndGhzIG1hdGNoLg0KICB0aGVHYWluIDwtIHRoZUdhaW5bdGhlR2FpbiA+IDBdDQogIHRoZUxvc3MgPC0gdGhlTG9zc1t0aGVMb3NzID4gMF0NCg0KICB0aGVHYWluIDwtIHRoZUdhaW4gKiBlZGdlU2NhbGUNCiAgdGhlTG9zcyA8LSB0aGVMb3NzICogZWRnZVNjYWxlDQoNCiAgcGFyb2xkIDwtIHBhcihuby5yZWFkb25seSA9IFRSVUUpDQogIHBhcihtZnJvdyA9IGMoMiwgMiksICMgVHdvIFJvd3MsIFR3byBDb2x1bW5zDQogICAgICBtYXIgPSBjKDAsIDEuNSwgMSwgMCksICMgTWFyZ2lucywgYm90dG9tLCBsZWZ0LCB0b3AsIHJpZ2h0DQogICAgICBvbWEgPSBjKDAuMSwgMC4xLCAwLjEsIDAuMSkgIyBPdXRlciBtYXJnaW5zLg0KICApDQoNCiAgY2hlZGRhcjo6UGxvdFdlYkJ5TGV2ZWwoDQogICAgdGhlR2MsDQogICAgc2hvdy5sZXZlbC5saW5lcyA9IFRSVUUsDQogICAgbGV2ZWwgPSAiTG9uZ1dlaWdodGVkVHJvcGhpY0xldmVsIg0KICApDQoNCiAgaWYgKCFyZWR1Y2VkVHJvcGhpYykgew0KICAgIHBsb3QoDQogICAgICB0aGVHaSwNCiAgICAgIGxheW91dCA9IHRoZUxheW91dCwNCiAgICAgIHZlcnRleC5zaXplID0gdGhlVnMsDQogICAgICBlZGdlLndpZHRoID0gMSwNCiAgICAgIGVkZ2UuYXJyb3cuc2l6ZSA9IDAuMywNCiAgICAgIGVkZ2UuYXJyb3cud2lkdGggPSAxLA0KICAgICAgdmVydGV4LmNvbG9yID0gdGhlQ29sb3JzLA0KICAgICAgZWRnZS5sdHkgPSAyLA0KICAgICAgZWRnZS5jb2xvciA9ICJncmV5IiwNCiAgICAgIGVkZ2UuYXJyb3cubW9kZSA9ICI+IiwNCiAgICAgIG1haW4gPSAiQ29uc3VtcHRpb24iDQogICAgKQ0KICB9IGVsc2Ugew0KICAgIEVWTGlzdFJlZCA8LSBFVkxpc3QNCiAgICBFVkxpc3RSZWQkRWRnZXMgPC0gRVZMaXN0UmVkJEVkZ2VzICU+JSBkcGx5cjo6ZmlsdGVyKA0KICAgICAgZWZmZWN0Tm9ybWFsaXNlZCA+PSB0aHJlc2hvbGQNCiAgICApDQogICAgdGhlR2MyIDwtIHRvQ2hlZGRhcihFVkxpc3RSZWQsIG5hbWUgPSAiU3Ryb25nZXN0IFRyb3BoaWMgTGV2ZWxzIikNCiAgICBjaGVkZGFyOjpQbG90V2ViQnlMZXZlbCgNCiAgICAgIHRoZUdjMiwNCiAgICAgIHNob3cubGV2ZWwubGluZXMgPSBUUlVFLA0KICAgICAgbGV2ZWwgPSAiTG9uZ1dlaWdodGVkVHJvcGhpY0xldmVsIg0KICAgICkNCiAgfQ0KDQogIHBsb3QoDQogICAgdGhlR2lHYWluLA0KICAgIGxheW91dCA9IHRoZUxheW91dCwNCiAgICB2ZXJ0ZXguc2l6ZSA9IHRoZVZzLA0KICAgIGVkZ2Uud2lkdGggPSB0aGVHYWluLA0KICAgIGVkZ2UuYXJyb3cuc2l6ZSA9IDAuMywNCiAgICBlZGdlLmFycm93LndpZHRoID0gMSwNCiAgICB2ZXJ0ZXguY29sb3IgPSB0aGVDb2xvcnMsDQogICAgZWRnZS5sdHkgPSAyLA0KICAgIGVkZ2UuY29sb3IgPSAiYmx1ZSIsDQogICAgZWRnZS5hcnJvdy5tb2RlID0gIj4iLA0KICAgIG1haW4gPSAiQ29uc3VtZXIncyBHYWlucyINCiAgKQ0KDQogIHBsb3QoDQogICAgdGhlR2lMb3NzLA0KICAgIGxheW91dCA9IHRoZUxheW91dCwNCiAgICB2ZXJ0ZXguc2l6ZSA9IHRoZVZzLA0KICAgIGVkZ2Uud2lkdGggPSB0aGVMb3NzLA0KICAgIGVkZ2UuYXJyb3cuc2l6ZSA9IDAuMywNCiAgICBlZGdlLmFycm93LndpZHRoID0gMiwNCiAgICB2ZXJ0ZXguY29sb3IgPSB0aGVDb2xvcnMsDQogICAgZWRnZS5sdHkgPSAzLA0KICAgIGVkZ2UuY29sb3IgPSAiZGFya3JlZCIsDQogICAgZWRnZS5hcnJvdy5tb2RlID0gIjwiLA0KICAgIG1haW4gPSAiUmVzb3VyY2UncyBMb3NzZXMiDQogICkNCiAgDQogIHBhcihwYXJvbGQpDQogIA0KICBFVkxpc3QkRWRnZXMgJT4lIGRwbHlyOjp1bmdyb3VwKCkgJT4lIGRwbHlyOjpmaWx0ZXIoDQogICAgZWZmZWN0Tm9ybWFsaXNlZCA+PSB0aHJlc2hvbGQNCiAgKSAlPiUgZHBseXI6OnNlbGVjdCgNCiAgICAtZWZmZWN0U2lnbg0KICApICU+JSBkcGx5cjo6YXJyYW5nZSgNCiAgICB0bywgLWVmZmVjdE5vcm1hbGlzZWQNCiAgKQ0KfQ0KDQpgYGANCg0KYGBge3J9DQp0aHJlc2hvbGRFZGdlcyA8LSAwLjMNCmBgYA0KDQpXZSB1c2UgYSB0aHJlc2hvbGQgb2YgYHIgdGhyZXNob2xkRWRnZXNgIGF0IGZpcnN0LCBmb2xsb3dlZCBieSBhbiBhZGFwdGl2ZSB0aHJlc2hvbGQuIEZvciB0aGUgYWRhcHRpdmUsIHdlIHVzZSB0aGUgc21hbGxlc3QgbGFyZ2VzdCBlZmZlY3Qgb2YgYSBnaXZlbiB0eXBlIGZvciBhIGdpdmVuIHJlY2lwaWVudC4gVG8gYnJlYWsgdGhhdCBkb3duLCB0aGUgbGFyZ2VzdCBlZmZlY3Qgb2YgYSBnaXZlbiB0eXBlIG1pZ2h0IGJlIHVzZWQgYXMgYSBwcm94eSBmb3IgaG93IHNwZWNpYWxpc3QgYSBnaXZlbiByZWNpcGllbnQncyBpbnRlcmFjdGlvbnMgYXJlLiBUaGUgc21hbGxlc3Qgb25lIG9mIHRoZXNlIGNhbiBiZSB0aG91Z2h0IG9mIGFzIHRoZSBtb3N0IGdlbmVyYWxpc3Qgc3BlY2llcyBpbiB0aGUgZ3JhcGgncyB0aHJlc2hvbGQgdG8gaGF2ZSBhdCBsZWFzdCBvbmUgZWRnZSBvZiBib3RoIHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSB0eXBlIGluY2x1ZGVkLg0KDQojIyMgR3JhcGggMQ0KYGBge3J9DQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbWzFdXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9IHRocmVzaG9sZEVkZ2VzKSAtPiB0ZW1wDQpgYGANCmBgYHtyfQ0KdGVtcA0KYGBgDQojIyMgR3JhcGggNQ0KYGBge3J9DQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbWzVdXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9IHRocmVzaG9sZEVkZ2VzKSAtPiB0ZW1wDQpgYGANCmBgYHtyfQ0KdGVtcA0KYGBgDQojIyMgR3JhcGggOQ0KYGBge3J9DQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbWzldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9IHRocmVzaG9sZEVkZ2VzKSAtPiB0ZW1wDQpgYGANCmBgYHtyfQ0KdGVtcA0KYGBgDQojIyMgR3JhcGggMSBBZGFwdGl2ZQ0KYGBge3J9DQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbWzFdXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCiMjIyBHcmFwaCA1IEFkYXB0aXZlDQpgYGB7cn0NCnRvUG9zdE1vcnRlbShmb29kV2Vic1tbNV1dLCBub2RlU2l6ZSA9ICJOb25lIiwgdGhyZXNob2xkID0gIkFkYXB0aXZlIikgLT4gdGVtcA0KYGBgDQpgYGB7cn0NCnRlbXANCmBgYA0KIyMjIEdyYXBoIDkgQWRhcHRpdmUNCmBgYHtyfQ0KdG9Qb3N0TW9ydGVtKGZvb2RXZWJzW1s5XV0sIG5vZGVTaXplID0gIk5vbmUiLCB0aHJlc2hvbGQgPSAiQWRhcHRpdmUiKSAtPiB0ZW1wDQpgYGANCmBgYHtyfQ0KdGVtcA0KYGBgDQo=